import * as fs from 'fs'
import * as path from 'path'

export default class FileUtil {
    static copyFile(source: string, destination: string): Promise<void> {
        return new Promise((resolve, reject) => {
            const rd = fs.createReadStream(source)
            rd.on('error', (err) => {
                reject(err)
            })

            const wr = fs.createWriteStream(destination)
            wr.on('error', (err) => {
                reject(err)
            })
            wr.on('close', () => {
                resolve()
            })

            if (!fs.existsSync(path.dirname(destination))) {
                fs.mkdirSync(path.dirname(destination), { recursive: true })
            }

            rd.pipe(wr)
        })
    }

    static async deleteFileOrDirectory(filePath: string): Promise<void> {
        try {
            const stats = await fs.promises.lstat(filePath)
            if (stats.isDirectory()) {
                const files = await fs.promises.readdir(filePath)
                const promises = files.map((file) => FileUtil.deleteFileOrDirectory(path.join(filePath, file)))
                await Promise.all(promises)
                await fs.promises.rmdir(filePath)
            } else {
                await fs.promises.unlink(filePath)
            }
        } catch (err) {}
    }

    static readFile(filePath: string): Promise<string> {
        return new Promise((resolve, reject) => {
            fs.readFile(filePath, 'utf8', (err, data) => {
                if (err) {
                    reject(err)
                } else {
                    resolve(data)
                }
            })
        })
    }

    static async readDirectory(
        directoryPath: string,
        fileCallback?: (filePath: string) => void | Promise<void>
    ): Promise<void> {
        try {
            const files = await fs.promises.readdir(directoryPath)
            const promises = files.map(async (file) => {
                const filePath = path.join(directoryPath, file)
                const stats = await fs.promises.stat(filePath)
                if (stats.isDirectory()) {
                    await FileUtil.readDirectory(filePath, fileCallback)
                } else {
                    if (fileCallback) {
                        await fileCallback(filePath)
                    }
                }
            })
            await Promise.all(promises)
        } catch (err) {
            throw err
        }
    }

    static async writeFile(filePath: string, content: string): Promise<void> {
        try {
            const directoryPath = path.dirname(filePath)
            await fs.promises.mkdir(directoryPath, { recursive: true })
            await fs.promises.writeFile(filePath, content, 'utf8')
        } catch (err) {
            throw err
        }
    }

    static async renameFileOrDirectory(oldPath: string, newPath: string): Promise<void> {
        try {
            await fs.promises.rename(oldPath, newPath)
        } catch (err) {
            throw err
        }
    }
}
